/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package com.google.code.biglo.engine;

import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.camel.CamelContext;
import org.apache.camel.Component;
import org.apache.camel.Consumer;
import org.apache.camel.Endpoint;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
import org.apache.camel.builder.xml.DomResultHandlerFactory;
import org.apache.camel.builder.xml.ResultHandler;
import org.apache.camel.impl.DefaultComponent;
import org.apache.camel.impl.DefaultConsumer;
import org.apache.camel.impl.DefaultEndpoint;
import org.apache.camel.impl.DefaultProducer;
import org.apache.commons.lang.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;

import com.google.code.biglo.api.BpmnEngine;
import com.google.code.biglo.compiler.BpmnCompiler;
import com.google.code.biglo.dao.BpmnDao;
import com.google.code.biglo.engine.BpmnEngineImpl.BpmnEndpoint;
import com.google.code.biglo.lang.BpmnDefinition;
import com.google.code.biglo.runtime.BpmnInstance;

public class BpmnEngineImpl extends DefaultComponent implements BpmnEngine, Component {
    private static Log log = LogFactory.getLog(BpmnEngineImpl.class);
    
    private BpmnDao bpmnDao;
    
    Map<String, BpmnDefinition> bpmnDefinitions = new HashMap<String, BpmnDefinition>();
    
    public void deployProcesses(List<Resource> resources) throws Exception {
        List<BpmnDefinition> processes = BpmnCompiler.compile(resources);
        registerProcesses(processes);
    }
    
    private void registerProcesses(List<BpmnDefinition> bpmnList) {
    	for (BpmnDefinition def : bpmnList) {
    		bpmnDefinitions.put(def.getId(), def);
    	}
    }
    
    public Long executeInstance(String processId, Date executionDate) {
    	BpmnDefinition bpmn = bpmnDefinitions.get(processId);
    	Validate.notNull(bpmn, "Asked for " + processId + ", but have " + bpmnDefinitions.keySet());
    	BpmnInstance i = BpmnInstance.create(bpmn, bpmnDao);
    	Long id = i.getInstanceDao().getId();
    	i.execute(executionDate);
    	return id;
    }

	public BpmnDao getBpmnDao() {
		return bpmnDao;
	}

	public void setBpmnDao(BpmnDao bpmnDao) {
		this.bpmnDao = bpmnDao;
	}

	@Override
	protected Endpoint createEndpoint(String uri, String service,
			Map<String, Object> args) throws Exception {
		log.debug("createEndpoint " + uri);
		BpmnEndpoint e = new BpmnEndpoint(uri, this, service);
		setProperties(e, args);
		return e;
	}
	
	public class BpmnConsumer extends DefaultConsumer {
		public BpmnConsumer(Endpoint endpoint, Processor processor) {
			super(endpoint, processor);
		}
	}
	
	public class BpmnProducer extends DefaultProducer {
		public BpmnProducer(Endpoint endpoint) {
			super(endpoint);
		}

		public void process(Exchange e) throws Exception {
        	ResultHandler resultHandler = new DomResultHandlerFactory().createResult(e);
            log.debug("Received exchange: " + resultHandler.getResult() + " on endpoint " + getEndpoint());
        	log.debug("Service: " + ((BpmnEndpoint) getEndpoint()).getService());
        	log.debug("Operation: " + ((BpmnEndpoint) getEndpoint()).getOperation());

		}
	}
	
	public class BpmnEndpoint extends DefaultEndpoint {
		private String service;
		private String operation;
		
		public BpmnEndpoint(String endpointUri, Component component, String service) {
			super(endpointUri, component);
			this.service = service;
		}

		public Consumer createConsumer(Processor arg0) throws Exception {
			return new BpmnConsumer(this, arg0);
		}

		public Producer createProducer() throws Exception {
			return new BpmnProducer(this);
		}

		public boolean isSingleton() {
			return false;
		}

		public String getOperation() {
			return operation;
		}

		public void setOperation(String operation) {
			this.operation = operation;
		}

		public String getService() {
			return service;
		}
	}
}
